﻿// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证（版本 2.0）进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动！任何基于本项目二次开发而产生的一切法律纠纷和责任，我们不承担任何责任！

using Aop.Api.Domain;
using Azure.Core;
using MQTTnet;
using MQTTnet.Packets;
using MQTTnet.Protocol;
using MQTTnet.Formatter;
using MQTTnet.Extensions.ManagedClient;
using Newtonsoft.Json;
using System.Text.Json;
using Novell.Directory.Ldap.Rfc2251;
using static QRCoder.PayloadGenerator;
using System.Collections.Concurrent;
using MQTTnet.Client;
using Elastic.Clients.Elasticsearch;
using Microsoft.AspNetCore.SignalR.Protocol;
using MaxMind.GeoIP2.Responses;
using Newtonsoft.Json.Linq;
using WCS.Application.Helper;
using SL.NET.Core.Service;
using WCS.Application.Entity.Warehous;
using System.Security.AccessControl;
using DocumentFormat.OpenXml.Drawing.Wordprocessing;
using WCS.Application.Service.Inventory;
using Furion.Schedule;
using System.Management.Automation;
using WCS.Application.Service.Plc;
using WCS.Application.Service.Inventory.DTO;
using System;
using Admin.NET.Application.Entity;
using static MyDemo.Application.Service.Plc.MqttService;
using Serilog;
using SL.NET.Core.Extensions;
using System.Reflection;
using Serilog.Events;
using NPOI.HPSF;

namespace MyDemo.Application.Service.Plc;
/// <summary>
/// MQ客户端
/// </summary>
[ApiDescriptionSettings(groups: ApplicationConst.GroupName, Order = 11)]
[AllowAnonymous]
public class MqttService: ITransient
{
    private IManagedMqttClient? _mqttClient { get; set; }
    public bool IsConnected => _mqttClient?.IsConnected ?? false;

    private ManagedMqttClientOptions _options;

    // 所有设备是否可用，根据输送线跟随心跳判断
    private bool _isAvailableHeart { get; set; }

    // x方向每两个坐标点之间的距离
    private readonly Dictionary<string, double> eachylength = new Dictionary<string, double>()
    {
        { "0,1",1181.5},
        { "1,2",1168.5},
        { "2,3",1181.5},
        { "3,4",1243.5},
        { "4,5",1223.5},
        { "5,6",1200},
        { "6,7",1200},
        { "7,8",1183},
        { "8,9",1243.5},
        { "9,10",1223.5},
        { "10,11",1200},
        { "11,12",1200},
        { "12,13",1183},
        { "13,14",1243.5},
        { "14,15",1223.5},
        { "15,16",1200},
        { "16,17",1175},
    };


    private readonly SysCacheService _sysCacheService;
    //private readonly InventorySFPService _inventorySFPService;
    //private readonly ISqlSugarRepository<MaintenanceEntity> _maintenancce;
    private readonly IServiceScopeFactory _scopeFactory;


    public MqttService(
        SysCacheService sysCacheService
        //,InventorySFPService inventorySFPService
        //,ISqlSugarRepository<MaintenanceEntity> maintenancce
        , IServiceScopeFactory scopeFactory
        )
    {
        _sysCacheService = sysCacheService;
        //_inventorySFPService = inventorySFPService;
        //_maintenancce = maintenancce;

        _scopeFactory = scopeFactory;

        // 异步启动客户端，避免构造函数阻塞
        _ = StartClientAsync();
    }

    public async Task StartClientAsync()
    {
        try
        {
            SerilogHelper.WriteLog("MqttService-StartClientAsync", "start");

            // 创建非单例服务
            try
            {
                    using var serviceScope = _scopeFactory.CreateScope();
                    var maintenceRep = serviceScope.ServiceProvider.GetService<SqlSugarRepository<MaintenanceEntity>>();

                    var list = await maintenceRep.Context.CopyNew()
                        .Queryable<MaintenanceEntity>()
                        .ToListAsync();
                    _sysCacheService.Set("qrDic", list.ToDictionary(it => it.Point.GetCString()));
                    _sysCacheService.Set("pointDic", list.ToDictionary(it => it.Vertical.GetCString() + "|" + it.Coordinate.GetCString() + "," + it.Abscissa.GetCString()));


                    //// 模拟测试自动反馈
                    //var plctest = serviceScope.ServiceProvider.GetService<PLCTest>();
                    //await plctest.RGVMove(DB40_taskNo, DB40_containerNo, device, device, "17", 0);
                
            }
            catch (Exception ex)
            {
                LogHelper.WriteErrorLog("MqttService-StartClientAsync", ex);
            }

            if (_mqttClient != null)
            {
                //await _mqttClient.StopAsync().ConfigureAwait(false);
                _mqttClient.Dispose();
            }

            _mqttClient = new MqttFactory().CreateManagedMqttClient();

            var ip = App.GetConfig<string>("MQTT:IP");
            var port = App.GetConfig<int>("MQTT:Port");
            var clientId = App.GetConfig<string>("MQTT:ClientId");
            var username = App.GetConfig<string>("MQTT:username");
            var password = App.GetConfig<string>("MQTT:password");
            SerilogHelper.WriteLog("MqttService-StartClientAsync", "ip", ip);
            SerilogHelper.WriteLog("MqttService-StartClientAsync", "port", port.GetCString());
            SerilogHelper.WriteLog("MqttService-StartClientAsync", "clientId", clientId);
            SerilogHelper.WriteLog("MqttService-StartClientAsync", "username", username);
            SerilogHelper.WriteLog("MqttService-StartClientAsync", "password", password);

            _options = new ManagedMqttClientOptionsBuilder()
                .WithAutoReconnectDelay(TimeSpan.FromSeconds(5))
                .WithMaxPendingMessages(int.MaxValue)
                .WithClientOptions(new MqttClientOptionsBuilder()
                    .WithClientId(string.IsNullOrEmpty(clientId) ? Guid.NewGuid().ToString() : clientId)
                    .WithTcpServer(ip, port)
                    .WithCredentials(username, password)
                    .WithTimeout(TimeSpan.FromSeconds(30))
                    .WithKeepAlivePeriod(TimeSpan.FromSeconds(60))
                    .WithProtocolVersion(MqttProtocolVersion.V311)
                    .WithCleanSession(true)
                    .Build()
                )
                .Build();


            _mqttClient.ConnectedAsync += Client_ConnectedAsync;
            _mqttClient.DisconnectedAsync += Client_DisconnectedAsync;
            _mqttClient.ApplicationMessageReceivedAsync += Client_ApplicationMessageReceivedAsync;

            await _mqttClient.StartAsync(_options).ConfigureAwait(false);

            SerilogHelper.WriteLog("MqttService-StartClientAsync", "connect");
        }
        catch (Exception ex)
        {
            LogHelper.WriteErrorLog("MqttService-StartClientAsync", ex);
        }
    }

    /// <summary>
    /// MQTT 连接事件，订阅主题
    /// </summary>
    /// <param name="arg"></param>
    /// <returns></returns>
    public async Task Client_ConnectedAsync(MqttClientConnectedEventArgs arg)
    {
        SerilogHelper.WriteLog("MQTT_Connect", "connect start");
        try
        {
            await _mqttClient.SubscribeAsync("devices/11/telemetry", MqttQualityOfServiceLevel.ExactlyOnce);
            await _mqttClient.SubscribeAsync("devices/12/telemetry", MqttQualityOfServiceLevel.ExactlyOnce);
            await _mqttClient.SubscribeAsync("devices/16/telemetry", MqttQualityOfServiceLevel.ExactlyOnce);
            await _mqttClient.SubscribeAsync("devices/17/telemetry", MqttQualityOfServiceLevel.ExactlyOnce);
            await _mqttClient.SubscribeAsync("devices/18/telemetry", MqttQualityOfServiceLevel.ExactlyOnce);
            await _mqttClient.SubscribeAsync("devices/19/telemetry", MqttQualityOfServiceLevel.ExactlyOnce);

            await _mqttClient.SubscribeAsync("devices/21/telemetry", MqttQualityOfServiceLevel.ExactlyOnce);
            await _mqttClient.SubscribeAsync("devices/22/telemetry", MqttQualityOfServiceLevel.ExactlyOnce);
            await _mqttClient.SubscribeAsync("devices/26/telemetry", MqttQualityOfServiceLevel.ExactlyOnce);
            await _mqttClient.SubscribeAsync("devices/27/telemetry", MqttQualityOfServiceLevel.ExactlyOnce);
            await _mqttClient.SubscribeAsync("devices/28/telemetry", MqttQualityOfServiceLevel.ExactlyOnce);
            await _mqttClient.SubscribeAsync("devices/29/telemetry", MqttQualityOfServiceLevel.ExactlyOnce);

            await _mqttClient.SubscribeAsync("devices/31/telemetry", MqttQualityOfServiceLevel.ExactlyOnce);
            await _mqttClient.SubscribeAsync("devices/32/telemetry", MqttQualityOfServiceLevel.ExactlyOnce);
            await _mqttClient.SubscribeAsync("devices/36/telemetry", MqttQualityOfServiceLevel.ExactlyOnce);
            await _mqttClient.SubscribeAsync("devices/37/telemetry", MqttQualityOfServiceLevel.ExactlyOnce);
            await _mqttClient.SubscribeAsync("devices/38/telemetry", MqttQualityOfServiceLevel.ExactlyOnce);
            await _mqttClient.SubscribeAsync("devices/39/telemetry", MqttQualityOfServiceLevel.ExactlyOnce);


            await _mqttClient.SubscribeAsync("devices/41/telemetry", MqttQualityOfServiceLevel.ExactlyOnce);
            await _mqttClient.SubscribeAsync("devices/42/telemetry", MqttQualityOfServiceLevel.ExactlyOnce);

            await _mqttClient.SubscribeAsync("devices/51/telemetry", MqttQualityOfServiceLevel.ExactlyOnce);
            await _mqttClient.SubscribeAsync("devices/52/telemetry", MqttQualityOfServiceLevel.ExactlyOnce);
            await _mqttClient.SubscribeAsync("devices/56/telemetry", MqttQualityOfServiceLevel.ExactlyOnce);
            await _mqttClient.SubscribeAsync("devices/57/telemetry", MqttQualityOfServiceLevel.ExactlyOnce);
            await _mqttClient.SubscribeAsync("devices/58/telemetry", MqttQualityOfServiceLevel.ExactlyOnce);
            await _mqttClient.SubscribeAsync("devices/59/telemetry", MqttQualityOfServiceLevel.ExactlyOnce);

            await _mqttClient.SubscribeAsync("internal/v1/gateway/telemetry/+/反馈状态", MqttQualityOfServiceLevel.ExactlyOnce);
            //await _mqttClient.SubscribeAsync("devices/+/rpc/request/+/+",  MqttQualityOfServiceLevel.ExactlyOnce);
            //await _mqttClient.SubscribeAsync("devices/+/attributes/update", MqttQualityOfServiceLevel.ExactlyOnce);
            //await _mqttClient.SubscribeAsync("devices/+/attributes/response/+", MqttQualityOfServiceLevel.ExactlyOnce);
        }
        catch (Exception ex)
        {
            LogHelper.WriteErrorLog("MQTT_Connect", ex);
        }
        SerilogHelper.WriteLog("MQTT_Connect", "connect end");
    }

    /// <summary>
    /// MQTT 断开连接事件
    /// </summary>
    /// <param name="arg"></param>
    /// <returns></returns>
    public Task Client_DisconnectedAsync(MqttClientDisconnectedEventArgs arg)
      {
        SerilogHelper.WriteLog("MQTT_Disconnect", "desconnect");

        // 异步启动客户端，避免构造函数阻塞
        _ = StartClientAsync();
        return Task.CompletedTask;
    }


    /// <summary>
    /// MQTT 消息接收事件
    /// </summary>
    /// <param name="arg"></param>
    /// <returns></returns>
    public async Task Client_ApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs arg)
    {
        SerilogHelper.WriteLog("MQTT_ReceiveMessage_message", "start");
        try
        {
            //SerilogHelper.WriteLog("MQTT_ReceiveMessage", "arg", JsonHelper.ObjectToJson(arg));
            var topic = arg.ApplicationMessage.Topic.GetCString();
            SerilogHelper.WriteLog("MQTT_ReceiveMessage_message", "topic", topic);


            // 点位数据
            var qrDic = _sysCacheService.Get<Dictionary<string, MaintenanceEntity>>("qrDic");
            if (qrDic is null)
            {
                using var serviceScope = _scopeFactory.CreateScope();
                var maintenceRep = serviceScope.ServiceProvider.GetService<SqlSugarRepository<MaintenanceEntity>>();

                var list = await maintenceRep.AsQueryable()
                    .ToListAsync();
                qrDic = list.ToDictionary(it => it.Point.GetCString());
                _sysCacheService.Set("qrDic", qrDic);
                _sysCacheService.Set("pointDic", list.ToDictionary(it => it.Vertical.GetCString() + "|" + it.Coordinate.GetCString() + "," + it.Abscissa.GetCString()));

            }

            // 四向车,订阅信息：internal/v1/gateway/telemetry/+/反馈状态
            if (topic.Contains("internal/v1/gateway/telemetry/"))
            {

                var device = topic.Split('/')[4];
                SerilogHelper.WriteLog("MQTT_ReceiveMessage_RGV", "device", device);
                var payload = arg.ApplicationMessage.ConvertPayloadToString();
                SerilogHelper.WriteLog("MQTT_ReceiveMessage_RGV", $"device:{device},payload", payload);

                try
                {
                    // 解析 JSON 字符串
                    JObject jo = JObject.Parse(payload);

                    var backStatus = jo["Value"].ToObject<List<ushort>>();
                    var deviceId = device;
                    var locationQRCode = backStatus[1]; // 当前二维码
                    var locationPoint = qrDic[locationQRCode.ToString()].Coordinate + "," + qrDic[locationQRCode.ToString()].Abscissa; // 当前坐标

                    var devices = _sysCacheService.Get<DerivesInfo>(device);
                    if (devices is null)
                        devices = new DerivesInfo();
                    devices.DeviceId = deviceId;
                    devices.DeviceStatus = 0;
                    devices.LocationQRCode = locationQRCode;
                    devices.LocationPoint = locationPoint;
                    devices.UpStatus = backStatus[5];

                    // 判断RGV指令状态位
                    if (backStatus[0] == 1)
                    {
                        devices.DeviceStatus = 1;
                    }

                    //// 充电位时，小车标记为有任务
                    //if (devices.UpStatus == 4)
                    //{
                    //    devices.DeviceStatus = 1;
                    //}


                    // 如果错误码不为0，反馈异常
                    if (backStatus[6] != 0)
                    {
                        devices.DeviceStatus = 2;
                    }

                    SerilogHelper.WriteLog("MQTT_ReceiveMessage_RGV", $"device:{device},设备可用状态", JsonHelper.ObjectToJson(devices));
                    _sysCacheService.Set(deviceId, devices);

                }
                catch (Exception ex)
                {
                    LogHelper.WriteErrorLog("MQTT_ReceiveMessage_RGV", ex);
                }

            }
            else
            {
                var device = topic.Split('/')[1];
                SerilogHelper.WriteLog("MQTT_ReceiveMessage_line", "device", device);
                var payload = arg.ApplicationMessage.ConvertPayloadToString();
                SerilogHelper.WriteLog("MQTT_ReceiveMessage_line", $"device:{device},payload", payload);

                var tps = arg.ApplicationMessage.Topic.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);

                //var topicPayloadCache = _sysCacheService.Get<string>(topic);
                //var ss = payload.Substring(0, payload.Length - 22);
                //if (topic.GetCString().Contains("四向车"))
                //{

                //    if (topicPayloadCache.GetCString() == payload.Substring(0, payload.Length - 22))
                //        return;
                //    _sysCacheService.Set(topic, payload.Substring(0, payload.Length - 22));
                //}
                //else
                //{
                //    if (topicPayloadCache.GetCString() == payload.Substring(0, payload.Length - 22))
                //        return;
                //    _sysCacheService.Set(topic, payload.Substring(0, payload.Length - 22));

                //}

                // 发送设备报文
                var requestPayload = new Dictionary<string, object>();
                switch (device.GetCString().ToLower())
                {
                    // 提升机输送线
                    case string d when d.Length == 2:
                        try
                        {
                            // 解析 JSON 字符串
                            JObject jsonObject = JObject.Parse(payload);

                            //// 设备状态
                            //var derivesInfo = new DerivesInfo
                            //{
                            //    DeviceId = tps[1],
                            //    DeviceType = 1,
                            //    DeviceStatus = 0
                            //};

                            // 获取设备任务状态
                            var derivesInfo = _sysCacheService.Get<DerivesInfo>(device);
                            if (derivesInfo is null)
                                derivesInfo = new DerivesInfo();
                            derivesInfo.DeviceId = device;
                            derivesInfo.DeviceStatus = 0;


                            // 如何没有跟随心跳，则认为所有提升机不可用
                            if (device == "10")
                            {
                                // 心跳
                                var wcsheartBeat = jsonObject["DB10_wcsheartBeat"].GetCInt();
                                var deviceheartBeat = jsonObject["DB10_deviceheartBeat"].GetCInt();

                                if (wcsheartBeat != deviceheartBeat)
                                {
                                    _isAvailableHeart = false;
                                }
                                else
                                {
                                    _isAvailableHeart = true;

                                    // 间隔10s写入一次心跳
                                    var heartCache = _sysCacheService.Get<string>("10Heart");
                                    if (heartCache is null)
                                    {
                                        _sysCacheService.Set("10Heart", (byte)Math.Abs(wcsheartBeat - 1), TimeSpan.FromSeconds(10));
                                    }
                                    else
                                    {
                                        break;
                                    }

                                    requestPayload.Add("DB10_wcsheartBeat", (byte)Math.Abs(wcsheartBeat - 1));
                                }

                                break;
                            }


                            var equipStatus = jsonObject["DB30_equipStatus"].GetCInt(); // 正常：1；异常：2，提升机锁定：3，（只针对提升机设备） 
                            var loadStatus = jsonObject["DB30_loadStatus"].GetCInt(); // 有货：1；无货：2 
                            var DB20_readFlag = jsonObject["DB20_readFlag"].GetCInt(); // 提升机DB20ReadFlag
                            var DB20_ackFlag = jsonObject["DB20_ackFlag"].GetCInt(); // 提升机DB20Command
                            var DB40_readFlag = jsonObject["DB40_readFlag"].GetCInt(); // DB40_readFlag
                            var DB40_ackFlag = jsonObject["DB40_ackFlag"].GetCInt(); // DB40_ackFlag

                            var DB40_command = jsonObject["DB40_command"].GetCInt(); // 提升机请求类型

                            var DB40_taskNo = jsonObject["DB40_taskNo"].GetCInt();
                            var DB40_deviceNo = jsonObject["DB40_deviceNo"].GetCInt();
                            var DB40_containerNo = jsonObject["DB40_containerNo"].GetCString();


                            derivesInfo.DB20_readFlag = DB20_readFlag;
                            derivesInfo.DB20_ackFlag = DB20_ackFlag;
                            derivesInfo.DB40_readFlag = DB40_readFlag;
                            derivesInfo.DB40_ackFlag = DB40_ackFlag;

                            // 提升机状态为正常，且当前没有任务时，设备设置为空闲可用状态
                            if (equipStatus == 1)
                            {
                                derivesInfo.DeviceStatus = 2;
                            }

                            // DB20_readFlag != DB40_ackFlag，输送线任务未处理完
                            if (DB20_readFlag != DB40_ackFlag)
                            {
                                derivesInfo.DeviceStatus = 1;
                            }

                            #region DB40_readFlag != DB20_ackFlag时，输送线有新的请求待处理

                            if (DB40_readFlag != DB20_ackFlag)
                            {
                                // 处理结束后，DB20_ackFlag跟随DB40_readFlag
                                requestPayload.Add("DB20_ackFlag", DB40_readFlag);

                                switch (DB40_command)
                                {
                                    // 1： 申请，扫码请求任务，
                                    case 1:
                                        // 托盘入库
                                        // 创建非单例服务
                                        {
                                            using var serviceScope = _scopeFactory.CreateScope();
                                            var inventorySFPService = serviceScope.ServiceProvider.GetService<InventorySFPService>();

                                            topic = $"devices/{device}/rpc/request/write/{DB40_taskNo}";
                                            // 判断提升机状态
                                            var deviceData = _sysCacheService.Get<DerivesInfo>(device);
                                            if (deviceData is null)
                                                deviceData = new DerivesInfo();
                                            //if (!_isAvailableHeart || deviceData.DeviceStatus == 1)
                                            //{
                                            //    // 逻辑待处理
                                            //    SerilogHelper.WriteLog("MQTT_PublishMessageAsync", "提升机当前有任务不可用", DB40_deviceNo.ToString());
                                            //}

                                            // readFlag交替写入1,2；根据DB40_ackFlag来判断
                                            byte readFlag = 1;
                                            if (deviceData.DB40_ackFlag == 1)
                                                readFlag = 2;
                                            // 如果托盘编码为空，则退回到11起始点
                                            if (DB40_containerNo.GetIsEmptyOrNull())
                                            {
                                                requestPayload.Add("DB20_command", (byte)1);
                                                requestPayload.Add("DB20_taskType", (byte)1);
                                                requestPayload.Add("DB20_deviceNo", DB40_deviceNo);
                                                requestPayload.Add("DB20_taskNo", DB40_taskNo);
                                                requestPayload.Add("DB20_startPosition", DB40_deviceNo);
                                                requestPayload.Add("DB20_targetPosition", (DB40_deviceNo.ToString().Substring(0, 1) + "1").GetCInt());
                                                requestPayload.Add("DB20_readFlag", readFlag);

                                            }
                                            else
                                            {

                                                var task = _sysCacheService.Get<string>(DB40_taskNo.ToString());
                                                if (task != null)
                                                {
                                                    SerilogHelper.WriteLog("MQTT_ReceiveMessage_line", $"device:{device},提升机当前有任务不可用，任务号{DB40_taskNo.ToString()}，", task.ToString());
                                                    requestPayload.Clear();

                                                    break;
                                                }

                                                var indata = new TemporarilyWarehouse
                                                {
                                                    TaskNo = DB40_taskNo,
                                                    Palcode = DB40_containerNo,
                                                    Hoistopening = device
                                                };
                                                var isSuccess = await inventorySFPService.TemporaryInbound(indata);
                                                if (isSuccess)
                                                {
                                                    _sysCacheService.Set(DB40_taskNo.ToString(), payload.Substring(0, payload.Length - 22), TimeSpan.FromSeconds(300));
                                                }
                                                else
                                                {
                                                    SerilogHelper.WriteLog("MQTT_ReceiveMessage_line", $"device:{device},生成出库异常：" + JsonHelper.ObjectToJson(indata));
                                                    _sysCacheService.Set(DB40_taskNo.ToString(), payload.Substring(0, payload.Length - 22), TimeSpan.FromSeconds(10));


                                                    requestPayload.Clear();
                                                }

                                            }


                                            //// 模拟测试自动反馈
                                            //var plctest = serviceScope.ServiceProvider.GetService<PLCTest>();
                                            //await plctest.RGVMove(DB40_taskNo, DB40_containerNo, device, device, "17", 0);
                                        }

                                        break;
                                    // 2： 离开（托盘离开任务起点，针对DB20内下发的是移动指令时才需要反馈该状态） 
                                    case 2:


                                        break;
                                    // 3：到达（托盘到达任务终点） ，
                                    case 3:
                                        //更新任务状态
                                        {

                                            // 下达指令后，存储目标设备的反馈状态
                                            var deviceResponseCache = _sysCacheService.Get<string>(device + "Response");
                                            if (deviceResponseCache != null)
                                            {
                                                //if (deviceResponseCache.StartsWith(DB40_taskNo.GetCString()))
                                                _sysCacheService.Set(device + "Response", DB40_taskNo.GetCString() + "|已反馈", TimeSpan.FromSeconds(300));
                                            }
                                        }


                                        break;

                                    // 允许出库占用（输送线允许四相车出库放货）
                                    case 4:

                                        break;
                                    // 满料上报(出口处满料，四相车须停止出料) 
                                    case 5:

                                        derivesInfo.DeviceStatus = 1;
                                        break;
                                    // 空料上报，(出口处空料，四相车可以出料) 
                                    case 6:

                                        derivesInfo.DeviceStatus = 0;
                                        break;

                                    // 提升机到达，(空车换曾时提升机到达指定层) 
                                    case 11:

                                        //derivesInfo.DeviceStatus = 0;
                                        break;

                                    // 提升机锁定失败，(提升机锁定失败)
                                    case 12:

                                        //derivesInfo.DeviceStatus = 0;
                                        break;

                                    default:
                                        SerilogHelper.WriteLog("MQTT_ReceiveMessage_line", $"device:{device},DB40_command错误", DB40_command.ToString());
                                        break;
                                }

                            }


                            #endregion

                            SerilogHelper.WriteLog("MQTT_ReceiveMessage_line", $"device:{device},设备可用状态", JsonHelper.ObjectToJson(derivesInfo));
                            _sysCacheService.Set(device, derivesInfo, TimeSpan.FromSeconds(3600));


                        }
                        catch (Exception ex)
                        {
                            LogHelper.WriteErrorLog("MQTT_ReceiveMessage_line", ex);
                        }
                        break;

                    //// 四向穿梭车
                    //case string d when d.Length == 7: // "四向车
                       
                    //    break;

                    default:

                        SerilogHelper.WriteLog("MQTT_ReceiveMessage_line", $"device:{device},Topic错误", topic);
                        break;


                }

                var publictopic = $"devices/{device}/rpc/request/write/{DateTime.Now.Ticks}";
                SerilogHelper.WriteLog("MQTT_ReceiveMessage_line", $"device:{device},publictopic", publictopic);
                SerilogHelper.WriteLog("MQTT_ReceiveMessage_line", $"device:{device},requestPayload", JsonHelper.ObjectToJson(requestPayload));

                if (requestPayload.Count > 0)
                {
                    SerilogHelper.WriteLog("MQTT_ReceiveMessage_line", $"device:{device},发送报文", JsonHelper.ObjectToJson(requestPayload));
                    await _mqttClient.EnqueueAsync(new MqttApplicationMessageBuilder()
                        .WithTopic(publictopic)
                        .WithPayload(JsonConvert.SerializeObject(requestPayload))
                        .WithQualityOfServiceLevel(MqttQualityOfServiceLevel.ExactlyOnce)
                        .Build());

                }
            }

        }
        catch (Exception ex)
        {
            LogHelper.WriteErrorLog("MQTT_ReceiveMessage_message_Error", ex);
        }
        SerilogHelper.WriteLog("MQTT_ReceiveMessage_message", "end");
    }


    /// <summary>
    /// 发送设备指令
    /// </summary>
    /// <param name="taskId">任务ID</param>
    /// <param name="agvTaskId">设备任务ID</param>
    /// <param name="palletCode">托盘码</param>
    /// <param name="device">设备编号</param>
    /// <param name="startPoint">起始点位</param>
    /// <param name="endPoint">目标点位</param>
    /// <param name="storey">当前层</param>
    /// <param name="actionType">动作类型：1: RGV移动；2: RGV举货；4：RGV放货；0：提升机上货；6：提升机下货</param>
    /// <returns></returns>
    public async Task<bool> PublishMessageAsync(string taskId, int agvTaskId, string palletCode, string device, string startPoint, string endPoint,int storey, int actionType)
    {
        SerilogHelper.WriteLog("PublishMessageAsync", "start");
        try
        {
            SerilogHelper.WriteLog("MQTT_PublishMessageAsync", "taskId", taskId.ToString());
            SerilogHelper.WriteLog("MQTT_PublishMessageAsync", "palletCode", palletCode);
            SerilogHelper.WriteLog("MQTT_PublishMessageAsync", "device", device);
            SerilogHelper.WriteLog("MQTT_PublishMessageAsync", "startPoint", startPoint);
            SerilogHelper.WriteLog("MQTT_PublishMessageAsync", "endPoint", endPoint);
            SerilogHelper.WriteLog("MQTT_PublishMessageAsync", "actionType", actionType.ToString());

            // 断线重连
            if (!IsConnected)
                await StartClientAsync();

            // 点位数据
            var pointDic = _sysCacheService.Get<Dictionary<string, MaintenanceEntity>>("pointDic");
            if(pointDic is null)
            {
                using var serviceScope = _scopeFactory.CreateScope();
                var maintenceRep = serviceScope.ServiceProvider.GetService<SqlSugarRepository<MaintenanceEntity>>();

                var list = await maintenceRep.AsQueryable()
                    .ToListAsync();
                pointDic = list.ToDictionary(it => it.Vertical.GetCString() + "|" + it.Coordinate.GetCString() + "," + it.Abscissa.GetCString());
                _sysCacheService.Set("qrDic", list.ToDictionary(it => it.Point.GetCString()));
                _sysCacheService.Set("pointDic", pointDic);
            }


            var derivesInfo = new DerivesInfo
            {
                DeviceId = device,
                TaskId = agvTaskId.ToString(),
                DeviceStatus = 1,
                DeviceType = 0,
            };

            var topic = $"devices/{device}/rpc/request/write/{agvTaskId}";
            var requestPayload = new Dictionary<string, object>();
            switch (actionType)
            {
                // RGV移动
                case 1:
                case 3:
                case 5:
                case 7:
                case 8:
                    {
                        // 获取设备任务状态
                        var deviceData = _sysCacheService.Get<DerivesInfo>(device);
                        if (deviceData is null)
                            deviceData = new DerivesInfo();
                        if (deviceData.DeviceStatus != 0)
                        {
                            SerilogHelper.WriteLog("MQTT_PublishMessageAsync", $"{device}当前设备未空闲,状态{deviceData.DeviceStatus}");
                            return false;
                        }

                        var startY = startPoint.Split(',')[0].GetCInt();
                        var startX = startPoint.Split(',')[1].GetCInt();

                        var endY = endPoint.Split(',')[0].GetCInt();
                        var endX = endPoint.Split(',')[1].GetCInt();


                        var stepnumber = Math.Abs((endY - startY) + (endX - startX));
                        double length = 0;
                        ushort direction = 0;

                        #region 计算行走方向，和行驶距离
                        // y不变：x增大，则是X+；x减小，X-
                        // x不变：y增大，则是Y+; y减小，Y-
                        // 1，2，3，4分别对应二维码X +,X -,Y -,Y +
                        if (startY == endY) //货格方向
                        {
                            // 距离等于行走的格子数 * 每两个单位之间的距离
                            if (endX > startX) // 向右
                            {
                                for (int i = startX; i < endX; i++)
                                {
                                    length += eachylength[$"{i},{i + 1}"];
                                }
                                direction = 2;
                            }
                            else
                            {
                                for (int i = endX; i < startX; i++)
                                {
                                    length += eachylength[$"{i},{i + 1}"];
                                }
                                direction = 1;
                            }

                            //if (length > 4775) // 待补充
                            //{
                            //    SerilogHelper.WriteLog("MQTT_PublishMessageAsync", "点位距离错误：", length.ToString());
                            //    return;

                            //}


                        }
                        else // 轨道方向
                        {
                            // 距离等于行走的格子数 * 每两个单位之间的距离,y方向每个两个二维码之间的距离是1350mm
                            length = stepnumber * 1350; 
                            if (endY > startY) // 向下
                            {
                                direction = 4;
                            }
                            else
                            {
                                direction = 3;
                            }

                            if (length > 40500) 
                            {
                                SerilogHelper.WriteLog("MQTT_PublishMessageAsync", "点位距离错误：", length.ToString());
                                return false;
                            }

                        }

                        #endregion

                        ushort startQR = 0;
                        ushort endQR = 0;

                        #region 转换起始坐标为二维码

                        startQR = (ushort)(pointDic[storey + "|" + startPoint].Point.GetCInt());
                        endQR = (ushort)(pointDic[storey + "|" + endPoint].Point.GetCInt());

                        #endregion

                        ushort[] values = {
                            1, // 控制指令字
                            startQR, // 起始二维码码值
                            0, // 中间二维码码值
                            endQR, // 目标二维码码值
                            (ushort)((int)length & 0xFFFF), // (ushort)((int)length & 0xFFFF), // 起点到目标点的距离长度，低16位
                            (ushort)(((int)length >> 16) & 0xFFFF), // (ushort)(((int)length >> 16) & 0xFFFF), // 起点到目标点的距离长度，高16位
                            0, // 中间点到目标点的距离长度，低16位
                            0, //中间点到目标点的距离长度，高16位
                            direction, //  direction, // RGV运行方向,可选1，2，3，4分别对应二维码X-,X+,Y-,Y+
                            0, // 顶升位置,1顶升位 2中间位 3换向位
                            0, // 强制行驶的距离长度，低16位
                            0, // 强制行驶的距离长度，低16位
                            0, // 
                            0, // 小车IO控制,Bit-1：顶升 Bit-2：换向 Bit-4：充电 
                            2400, // 小车运行速度
                            0, // 
                            1 // 指令结束位,每次下发指令都必须将此位置写入1，RGV才会执行指令，否则视为指令未正常下发
                        };
                        requestPayload.Add("读写指令", JsonHelper.ObjectToJson(values));

                        break;
                    }
                // RGV举货
                case 2:
                case 9:
                    {
                        ushort[] values = {
                            2, // 控制指令字
                            0, // 起始二维码码值
                            0, // 中间二维码码值
                            0, // 目标二维码码值
                            0, // 起点到目标点的距离长度，低16位
                            0, // 起点到目标点的距离长度，高16位
                            0, // 中间点到目标点的距离长度，低16位
                            0, //中间点到目标点的距离长度，高16位
                            0, // RGV运行方向,可选1，2，3，4分别对应二维码X+,X-,Y-,Y+
                            1, // 顶升位置,1顶升位 2中间位 3换向位
                            0, // 强制行驶的距离长度，低16位
                            0, // 强制行驶的距离长度，低16位
                            0, // 
                            0, // 小车IO控制,Bit-1：顶升 Bit-2：换向 Bit-4：充电 
                            0, // 小车运行速度
                            0, // 
                            1 // 指令结束位,每次下发指令都必须将此位置写入1，RGV才会执行指令，否则视为指令未正常下发
                        };
                        requestPayload.Add("读写指令", JsonHelper.ObjectToJson(values));

                        break;
                    }
                // RGV放货
                case 4:
                    {
                        ushort[] values = {
                            2, // 控制指令字
                            0, // 起始二维码码值
                            0, // 中间二维码码值
                            0, // 目标二维码码值
                            0, // 起点到目标点的距离长度，低16位
                            0, // 起点到目标点的距离长度，高16位
                            0, // 中间点到目标点的距离长度，低16位
                            0, //中间点到目标点的距离长度，高16位
                            0, // RGV运行方向,可选1，2，3，4分别对应二维码X+,X-,Y-,Y+
                            2, // 顶升位置,1顶升位 2中间位 3换向位
                            0, // 强制行驶的距离长度，低16位
                            0, // 强制行驶的距离长度，低16位
                            0, // 
                            0, // 小车IO控制,Bit-1：顶升 Bit-2：换向 Bit-4：充电 
                            0, // 小车运行速度
                            0, // 
                            1 // 指令结束位,每次下发指令都必须将此位置写入1，RGV才会执行指令，否则视为指令未正常下发
                        };
                        requestPayload.Add("读写指令", JsonHelper.ObjectToJson(values));

                        break;
                    }
                // 提升机上货
                case 0:
                    {
                        device = startPoint;
                        topic = $"devices/{device}/rpc/request/write/{taskId}";
                        // 判断提升机状态
                        var deviceData = _sysCacheService.Get<DerivesInfo>(device);
                        if (deviceData is null)
                            deviceData = new DerivesInfo();
                        if (!_isAvailableHeart || deviceData.DeviceStatus == 1)
                        {
                            // 逻辑待处理
                            SerilogHelper.WriteLog("MQTT_PublishMessageAsync", "提升机当前有任务不可用", startPoint);
                        }


                        // readFlag交替写入1,2；根据DB40_ackFlag来判断
                        byte readFlag = 1;
                        if (deviceData.DB20_readFlag == 1)
                            readFlag = 2;

                        var devices = App.GetConfig<List<DerivesInfo>>("MQTT:Devices");
                        var startDevice = devices.FirstOrDefault(it => it.DeviceType == 1
                                            && it.DeviceGroup != null
                                            && it.DeviceGroup.Contains(startPoint));
                        if (startDevice != null)
                            startPoint = startDevice.InboudStartDevice;
                        else
                            SerilogHelper.WriteLog("MQTT_PublishMessageAsync", "未找到提升机分组信息", startPoint);

                        //requestPayload.Add("DB20_ackFlag", ackFlag);
                        requestPayload.Add("DB20_command", (byte)1);
                        requestPayload.Add("DB20_taskType", (byte)1);
                        requestPayload.Add("DB20_deviceNo", startPoint);
                        requestPayload.Add("DB20_taskNo", taskId);
                        requestPayload.Add("DB20_containerNo", palletCode.PadRight(8));
                        requestPayload.Add("DB20_startPosition", startPoint);
                        requestPayload.Add("DB20_targetPosition", endPoint);


                        await _mqttClient.EnqueueAsync(new MqttApplicationMessageBuilder()
                            .WithTopic(topic)
                            .WithPayload(JsonConvert.SerializeObject(requestPayload))
                            .WithQualityOfServiceLevel(MqttQualityOfServiceLevel.ExactlyOnce)
                            .Build());
                        requestPayload.Add("DB20_readFlag", readFlag);
                        //requestPayload.Add("DB20_Rel0", values.ToString());

                        // 下达指令后，存储目标设备的反馈状态
                        var deviceResponseCache = _sysCacheService.Get<string>(endPoint + "Response");
                        if (deviceResponseCache == null)
                        {
                            _sysCacheService.Set(endPoint + "Response", taskId + "|待反馈", TimeSpan.FromSeconds(300));
                        }
                        break;
                    }
                // 提升机下货
                case 6:
                    {
                        device = startPoint;
                        topic = $"devices/{device}/rpc/request/write/{taskId}";
                        var devices = App.GetConfig<List<DerivesInfo>>("MQTT:Devices");
                        var endDevice = devices.FirstOrDefault(it => it.DeviceType == 1
                                            && it.DeviceGroup != null
                                            && it.DeviceGroup.Contains(endPoint));
                        if (endDevice != null)
                            endPoint = endDevice.OutboudEndDevice;
                        else
                            SerilogHelper.WriteLog("MQTT_PublishMessageAsync", "未找到提升机分组信息", endPoint);


                        // 判断提升机状态
                        var deviceData = _sysCacheService.Get<DerivesInfo>(startPoint);
                        if (deviceData is null)
                            deviceData = new DerivesInfo();
                        if (!_isAvailableHeart || deviceData.DeviceStatus == 1)
                        {
                            // 逻辑待处理
                            SerilogHelper.WriteLog("MQTT_PublishMessageAsync", "提升机当前有任务不可用", startPoint);
                        }

                        // readFlag交替写入1,2；根据DB40_ackFlag来判断
                        byte readFlag = 1;
                        if (deviceData.DB20_readFlag == 1)
                            readFlag = 2;



                        //requestPayload.Add("DB20_ackFlag", readFlag);
                        requestPayload.Add("DB20_command", (byte)1);
                        requestPayload.Add("DB20_taskType", (byte)1);
                        requestPayload.Add("DB20_deviceNo", startPoint);
                        requestPayload.Add("DB20_taskNo", taskId);
                        requestPayload.Add("DB20_containerNo", palletCode.PadRight(8));
                        requestPayload.Add("DB20_startPosition", startPoint);
                        requestPayload.Add("DB20_targetPosition", endPoint);
                        requestPayload.Add("DB20_readFlag", readFlag);
                        //requestPayload.Add("DB20_Rel0", values.ToString());

                        // 下达指令后，存储目标设备的反馈状态
                        var deviceResponseCache = _sysCacheService.Get<string>(endPoint + "Response");
                        if (deviceResponseCache == null)
                        {
                            _sysCacheService.Set(endPoint + "Response", taskId + "|待反馈", TimeSpan.FromSeconds(300));
                        }
                        break;
                    }
                // 充电
                case 10:
                    {
                        ushort[] values = {
                            8, // 控制指令字
                            0, // 起始二维码码值
                            0, // 中间二维码码值
                            0, // 目标二维码码值
                            0, // 起点到目标点的距离长度，低16位
                            0, // 起点到目标点的距离长度，高16位
                            0, // 中间点到目标点的距离长度，低16位
                            0, //中间点到目标点的距离长度，高16位
                            0, // RGV运行方向,可选1，2，3，4分别对应二维码X+,X-,Y-,Y+
                            0, // 顶升位置,1顶升位 2中间位 3换向位
                            0, // 强制行驶的距离长度，低16位
                            0, // 强制行驶的距离长度，低16位
                            0, // 
                            16, // 小车IO控制,Bit-1：顶升 Bit-2：换向 Bit-4：充电 
                            0, // 小车运行速度
                            0, // 
                            1 // 指令结束位,每次下发指令都必须将此位置写入1，RGV才会执行指令，否则视为指令未正常下发
                        };
                        requestPayload.Add("读写指令", JsonHelper.ObjectToJson(values));

                        break;
                    }
                default:
                    SerilogHelper.WriteLog("MQTT_PublishMessageAsync", "动作类型错误：actionType", actionType.ToString());
                    break;
            }


            // 下达指令后，设备状态设置为有任务状态
            derivesInfo.DeviceStatus = 1;
            SerilogHelper.WriteLog("MQTT_PublishMessageAsync", "derivesInfo", JsonHelper.ObjectToJson(derivesInfo));
            _sysCacheService.Set(device, derivesInfo, TimeSpan.FromSeconds(3600));



            SerilogHelper.WriteLog("MQTT_PublishMessageAsync", "topic", topic);
            SerilogHelper.WriteLog("MQTT_PublishMessageAsync", "requestPayload", JsonHelper.ObjectToJson(requestPayload));

            await _mqttClient.EnqueueAsync(new MqttApplicationMessageBuilder()
                .WithTopic(topic)
                .WithPayload(JsonConvert.SerializeObject(requestPayload))
                .WithQualityOfServiceLevel(MqttQualityOfServiceLevel.ExactlyOnce)
                .Build());

            SerilogHelper.WriteLog("PublishMessageAsync", "end");
            return true;
        }
        catch (Exception ex)
        {
            SerilogHelper.WriteLog("PublishMessageAsync", "end");
            LogHelper.WriteErrorLog("MQTT_PublishMessageAsync_Error", ex);
            return false;
        }

    }

    /// <summary>
    /// 获取redsi 节点和k
    /// </summary>
    /// <param name="topic"></param>
    /// <param name="value"></param>
    /// <returns></returns>
    [ApiDescriptionSettings(Name = "RedisGet"), HttpPost]
    public Task<string> RedisGet(string topic, string value = "")
    {
        var topicjson = "";
        var c = _sysCacheService.GetKeyList();
        if (_sysCacheService.ExistKey(topic))
        {
            topicjson = _sysCacheService.Get<string>(topic);
            if (topicjson == null)
            {
                //从redis抓紧获取
                throw Oops.Oh($"Redis为存放ksy为{topic}的，请稍后再试");
            }
        }
        else
        {
            throw Oops.Oh($"Redis无此{topic}，请稍后再试");

        }
        string jsonText = "{\"ClassName\":\"Science\",\"Final\":true,\"Semester\":\"2019-01-01\",\"Students\":[{\"Name\":\"John\",\"Grade\":94.3},{\"Name\":\"James\",\"Grade\":81.0},{\"Name\":\"Julia\",\"Grade\":91.9},{\"Name\":\"Jessica\",\"Grade\":72.4},{\"Name\":\"Johnathan\"}],\"Teacher'sName\":\"Jane\"}";
        JsonDocument doc = JsonDocument.Parse(topicjson);
        JsonElement root = doc.RootElement;

        // 将 root 转为动态对象
        dynamic dynamicObject = JsonConvert.DeserializeObject<dynamic>(root.GetRawText());

        if (value != "")
        {
            SerilogHelper.WriteLog("", $"topic from Redis: {topic},字段{value}:{dynamicObject[value]}");
            return Task.FromResult(dynamicObject[value]?.ToString());
        }
        else
        {
            SerilogHelper.WriteLog("", $"topic from Redis: {topic},{value}:");
            return Task.FromResult(dynamicObject.ToString());
        }
    }


    public async Task StopAsync(CancellationToken cancellationToken)
    {
        try
        {
            if (_mqttClient.IsConnected)
            {
                // 创建断开连接选项
                var disconnectOptions = new MqttClientDisconnectOptions
                {
                    Reason = (MqttClientDisconnectOptionsReason)MqttClientDisconnectReason.NormalDisconnection,
                    ReasonString = "Service is being stopped.停止"
                };

                //await _mqttClient.DisconnectAsync(disconnectOptions, cancellationToken);
                SerilogHelper.WriteLog("StopAsync", "Disconnected from MQTT server.");
            }
        }
        catch (Exception ex)
        {
            LogHelper.WriteErrorLog("StopAsync", ex);
        }
    }


    public class DerivesInfo
    {
        /// <summary>
        /// 设备ID
        /// </summary>
        public string DeviceId { get; set; }

        /// <summary>
        /// 设备任务ID
        /// </summary>
        public string TaskId { get; set; }

        /// <summary>
        /// 空闲状态，0 ：空闲，1：有任务，2：异常
        /// </summary>
        public int DeviceStatus { get; set; }

        /// <summary>
        /// 设备可用状态
        /// </summary>
        public bool DeviceEnable { get; set; }

        /// <summary>
        /// 举货状态
        /// </summary>
        public int UpStatus { get; set; }

        /// <summary>
        /// 设备类型：0：四向车，1：提升机
        /// </summary>
        public int DeviceType { get; set; }

        /// <summary>
        /// 设备组(只有提升机有用）
        /// </summary>
        public List<string> DeviceGroup { get; set; }

        /// <summary>
        /// 入库时，提升机开始设备ID
        /// </summary>
        public string InboudStartDevice { get; set; }

        /// <summary>
        /// 出库时，提升机结束设备ID
        /// </summary>
        public string OutboudEndDevice { get; set; }

        /// <summary>
        ///  提升机对应的坐标,格式0,0
        /// </summary>
        public string Point{ get; set; }

        /// <summary>
        /// 四向车当前二维码
        /// </summary>
        public int LocationQRCode { get; set; }

        /// <summary>
        /// 四向车当前坐标0,0格式
        /// </summary>
        public string LocationPoint { get; set; }

        /// <summary>
        /// DB20_readFlag
        /// </summary>
        public int DB20_readFlag { get; set; }

        /// <summary>
        /// DB20_acklag
        /// </summary>
        public int DB20_ackFlag { get; set; }

        /// <summary>
        /// 提升机DB40_readFlag
        /// </summary>
        public int DB40_readFlag { get; set; }

        /// <summary>
        /// DB40_ackFlag
        /// </summary>
        public int DB40_ackFlag { get; set; }


        /// <summary>
        /// 当前RGV的层
        /// </summary>
        public int Storey { get; set; }
    }


}

///// <summary>  
///// 统一的反馈结果类  
///// </summary>  
//public class MqttResponseResult
//{
//    /// <summary>  
//    /// 是否成功  
//    /// </summary>  
//    public bool IsSuccess { get; set; }

//    /// <summary>  
//    /// 返回的消息  
//    /// </summary>  
//    public string Message { get; set; }

//    /// <summary>  
//    /// 返回的数据  
//    /// </summary>  
//    public object Data { get; set; }

//    /// <summary>  
//    /// 构造成功的反馈结果  
//    /// </summary>  
//    /// <param name="data">返回的数据</param>  
//    /// <param name="message">返回的消息</param>  
//    /// <returns>MqttResponseResult</returns>  
//    public static MqttResponseResult Success(object data = null, string message = "操作成功")
//    {
//        return new MqttResponseResult
//        {
//            IsSuccess = true,
//            Message = message,
//            Data = data
//        };
//    }

//    /// <summary>  
//    /// 构造失败的反馈结果  
//    /// </summary>  
//    /// <param name="message">错误消息</param>  
//    /// <returns>MqttResponseResult</returns>  
//    public static MqttResponseResult Failure(string message = "操作失败")
//    {
//        return new MqttResponseResult
//        {
//            IsSuccess = false,
//            Message = message,
//            Data = null
//        };
//    }
//}
